package com.swak.paxos.statemachine.system;

import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.paxos.PaxosConfig.GroupConfig;
import com.swak.paxos.Value;
import com.swak.paxos.statemachine.StateMachine;
import com.swak.utils.Sets;
import com.swak.utils.StringUtils;

import lombok.Getter;

/**
 * 系统状态机；
 * 
 * @author DELL
 */
public class SystemxStateMachine implements StateMachine {

	private final Logger logger = LoggerFactory.getLogger(SystemxStateMachine.class);

	private final String nodeID;
	private final GroupConfig config;

	/**
	 * 节点发生变化时，设置为false，且向集群同步最新的节点信息
	 */
	private volatile boolean accordant = false;

	/**
	 * 本节点的节点关系; (没有存储 Follower 节点)
	 */
	private final Set<String> memberShips = Sets.newHashSet();

	/**
	 * Follower 节点
	 */
	@Getter
	private final Set<String> followerShips = Sets.newHashSet();

	/**
	 * 系统一致性的状态
	 */
	private Systemx systemx = new Systemx();

	public SystemxStateMachine(String nodeID, GroupConfig config) {

		// 当前节点信息
		this.nodeID = nodeID;
		this.config = config;

		// 当前节点设置到同步集合
		this.addSyncNode(nodeID);

		// 其他节点设置到同步集合, 其他节点默认不是 Follower 节点
		// 可以使用本地存储，启动时从文件中加载
		for (String otherNodeID : config.getNodes()) {
			this.addSyncNode(otherNodeID);
		}
	}

	private void addSyncNode(String nodeID) {

		// 当前节点
		if ((this.nodeID.equals(nodeID) && this.config.isIMFollower()) || StringUtils.isBlank(nodeID)) {
			return;
		}

		// 添加节点
		this.systemx.getMemberShips().add(nodeID);
	}

	/**
	 * 添加节点
	 * 
	 * @param nodeID
	 */
	public void addMember(String nodeID, GroupConfig config) {
		if (config == null || config.isIMFollower()) {
			if (config.isIMFollower()) {
				this.followerShips.add(nodeID);
			}
			this.memberShips.remove(nodeID);
		} else {
			this.followerShips.remove(nodeID);
			this.memberShips.add(nodeID);
		}
		this.accordant = false;
	}

	/**
	 * 删除节点
	 * 
	 * @param nodeID
	 */
	public void removeMember(String nodeID, GroupConfig config) {
		if (config == null || config.isIMFollower()) {
			this.followerShips.remove(nodeID);
			return;
		}
		this.memberShips.remove(nodeID);
		this.accordant = false;
	}

	/**
	 * 系统状态是否一致；
	 * 
	 * @return
	 */
	public boolean isAccordant() {
		return this.accordant;
	}

	/**
	 * 需要保持一致的数据
	 * 
	 * @return
	 */
	public Systemx needAccordantValue() {
		return new Systemx().setVersion(systemx.getVersion()).setMemberShips(memberShips);
	}

	/**
	 * 同步集群的状态;
	 * 
	 * @return
	 */
	public int nodeCount() {
		return this.systemx.getMemberShips().size();
	}

	/**
	 * 过半数
	 * 
	 * @param nodeID
	 * @return
	 */
	public int majorityCount() {
		return (int) (Math.floor(((double) nodeCount() / 2)) + 1);
	}

	/**
	 * 是否处理同步关系中;
	 * 
	 * @param nodeID
	 * @return
	 */
	public boolean inMemberShip(String nodeID) {
		return this.systemx.getMemberShips().contains(nodeID);
	}

	/**
	 * 当前节点是否处理同步关系中;
	 * 
	 * @return
	 */
	public boolean isIMInMembership() {
		return this.systemx.getMemberShips().contains(this.nodeID);
	}

	/**
	 * 启动了过半的节点，才能发起 Paxos 算法;
	 * 
	 * @return
	 */
	public boolean isValidMembership() {
		int syncs = 0;
		for (String nodeID : this.systemx.getMemberShips()) {
			syncs += this.memberShips.contains(nodeID) ? 1 : 0;
		}
		return syncs >= this.majorityCount();
	}

	@Override
	public int getSmID() {
		return SystemSmID;
	}

	@Override
	public boolean execute(int groupID, long instanceID, Value value) {

		/*
		 * 协调的数据
		 */
		Systemx Systemx = value.getEstimateValue();

		/*
		 * 版本校验
		 */
		if (Systemx.getVersion() != this.systemx.getVersion()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Version not same.");
			}
			return true;
		}

		/*
		 * 更新数据
		 */
		Systemx.setVersion(instanceID);
		this.systemx = Systemx;
		this.accordant = true;

		/*
		 * 打印调试信息
		 */
		if (logger.isDebugEnabled()) {
			logger.debug("Systemx info: " + Systemx);
		}
		return true;
	}
}